<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useExpressionStore, type ExpressionEntry } from '@/stores/expressionStore'

const expressionStore = useExpressionStore()
const { expressions } = storeToRefs(expressionStore)

const hasExpressions = computed(() => expressions.value.length > 0)

function addExpression() {
  expressionStore.addExpression('')
}

function removeExpression(id: string) {
  expressionStore.removeExpression(id)
}

function toggleVisibility(entry: ExpressionEntry) {
  expressionStore.updateExpression({ id: entry.id, visible: !entry.visible })
}

function updateExpressionValue(entry: ExpressionEntry, value: string) {
  expressionStore.updateExpression({ id: entry.id, expression: value })
}

function updateColor(entry: ExpressionEntry, color: string) {
  expressionStore.updateExpression({ id: entry.id, color })
}

function formatReadyStatus(entry: ExpressionEntry): string {
  const segments: string[] = []
  segments.push(`段数 ${entry.segmentCount}`)

  if (entry.sampleValue !== null) {
    segments.push(`中心值 ≈ ${entry.sampleValue.toFixed(3)}`)
  }

  if (entry.lastDurationMs !== null) {
    segments.push(`耗时 ${entry.lastDurationMs.toFixed(1)} ms`)
  }

  if (entry.validSampleRatio !== null) {
    if (entry.validSampleRatio <= 0) {
      segments.push('当前视图无定义域')
    } else if (entry.validSampleRatio < 0.999) {
      segments.push(`有效率 ${(entry.validSampleRatio * 100).toFixed(0)}%`)
    }
  }

  segments.push(entry.cacheHit ? '缓存命中' : '重新计算')
  return segments.join(' · ')
}

const ITEM_HEIGHT = 240
const OVERSCAN = 1
const scrollTop = ref(0)
const viewportRef = ref<HTMLDivElement | null>(null)

const totalHeight = computed(() => Math.max(0, expressions.value.length * ITEM_HEIGHT))
const maxScrollTop = computed(() => Math.max(0, totalHeight.value - ITEM_HEIGHT))

const startIndex = computed(() => {
  const index = Math.floor(scrollTop.value / ITEM_HEIGHT) - OVERSCAN
  return Math.max(0, index)
})

const endIndex = computed(() => {
  const base = startIndex.value + 1 + OVERSCAN * 2
  return Math.min(expressions.value.length, base)
})

const virtualExpressions = computed(() => {
  return expressions.value.slice(startIndex.value, endIndex.value)
})

const translateY = computed(() => startIndex.value * ITEM_HEIGHT)

function handleScroll(event: Event) {
  const target = event.target as HTMLDivElement
  scrollTop.value = target.scrollTop
}

watch(
  () => expressions.value.length,
  (newLength, oldLength) => {
    if (newLength > oldLength) {
      nextTick(() => {
        const viewport = viewportRef.value
        if (!viewport) {
          return
        }
        viewport.scrollTop = Math.max(0, totalHeight.value - ITEM_HEIGHT)
      })
    } else {
      scrollTop.value = Math.min(scrollTop.value, maxScrollTop.value)
    }
  },
)

watch(maxScrollTop, (limit) => {
  if (scrollTop.value > limit) {
    scrollTop.value = limit
    const viewport = viewportRef.value
    if (viewport) {
      viewport.scrollTop = limit
    }
  }
})
</script>

<template>
  <section class="expression-panel">
    <header class="panel-header">
      <div>
        <h2 class="panel-title">表达式</h2>
        <p class="panel-subtitle">
          输入 F(x, y) = 0 形式的表达式生成图线，支持加减乘除、幂运算及常见初等函数。
        </p>
      </div>
      <button class="panel-action" type="button" @click="addExpression">
        + 添加表达式
      </button>
    </header>

    <div v-if="!hasExpressions" class="empty-state">
      <p>暂未添加表达式。点击上方按钮创建一个新的表达式。</p>
    </div>

    <div v-else class="expression-list-wrapper">
      <div
        ref="viewportRef"
        class="expression-list-viewport"
        :style="{ height: `${ITEM_HEIGHT}px` }"
        @scroll.passive="handleScroll"
      >
        <div class="expression-virtual-spacer" :style="{ height: `${totalHeight}px` }">
          <ul
            class="expression-list expression-list--virtual"
            :style="{ transform: `translateY(${translateY}px)` }"
          >
            <li v-for="entry in virtualExpressions" :key="entry.id" class="expression-item">
              <div class="expression-head">
                <button
                  type="button"
                  class="visibility-toggle"
                  :class="{ 'visibility-toggle--off': !entry.visible }"
                  @click="toggleVisibility(entry)"
                  :title="entry.visible ? '隐藏图线' : '显示图线'"
                >
                  {{ entry.visible ? '👁' : '🚫' }}
                </button>

                <input
                  class="color-input"
                  type="color"
                  :value="entry.color"
                  aria-label="图线颜色"
                  @input="(event) => updateColor(entry, (event.target as HTMLInputElement).value)"
                />

                <div class="status-indicator" :data-status="entry.status">
                  <span v-if="entry.status === 'evaluating'">计算中…</span>
                  <span v-else-if="entry.status === 'ready'">
                    {{ formatReadyStatus(entry) }}
                  </span>
                  <span v-else-if="entry.status === 'error'">
                    {{ entry.errorMessage ?? '解析失败' }}
                  </span>
                  <span v-else>待计算</span>
                </div>
              </div>

              <textarea
                class="expression-input"
                :value="entry.expression"
                placeholder="例如：x^2 + y^2 = 1"
                rows="2"
                @input="(event) => updateExpressionValue(entry, (event.target as HTMLTextAreaElement).value)"
              />

              <footer class="expression-footer">
                <button class="expression-remove" type="button" @click="removeExpression(entry.id)">删除</button>
              </footer>

              <p v-if="entry.status === 'error' && entry.errorMessage" class="expression-error">
                {{ entry.errorMessage }}
              </p>
            </li>
          </ul>
        </div>
      </div>
    </div>
  </section>
</template>

<style scoped>
.expression-panel {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.panel-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.panel-title {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 600;
}

.panel-subtitle {
  margin: 0.35rem 0 0;
  font-size: 0.85rem;
  color: #94a3b8;
  max-width: 28rem;
}

.panel-action {
  background: #38bdf8;
  color: #0b1120;
  border: none;
  border-radius: 0.5rem;
  padding: 0.5rem 0.85rem;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  transition: transform 120ms ease, box-shadow 120ms ease;
}

.panel-action:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 20px rgba(56, 189, 248, 0.2);
}

.expression-list-wrapper {
  position: relative;
  border-radius: 0.75rem;
}

.expression-list-viewport {
  width: 100%;
  overflow-y: auto;
  border-radius: 0.75rem;
  scrollbar-width: thin;
}

.expression-virtual-spacer {
  position: relative;
}

.expression-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.expression-list--virtual {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding-right: 0.5rem;
  box-sizing: border-box;
}

.expression-item {
  background: rgba(15, 23, 42, 0.6);
  border: 1px solid rgba(148, 163, 184, 0.16);
  border-radius: 0.75rem;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.expression-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.visibility-toggle {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 0.6rem;
  background: rgba(148, 163, 184, 0.18);
  color: #e2e8f0;
  border: none;
  cursor: pointer;
  font-size: 1.1rem;
  transition: background 150ms ease;
}

.visibility-toggle--off {
  background: rgba(148, 163, 184, 0.08);
  color: #64748b;
}

.color-input {
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border-radius: 0.5rem;
  border: none;
  overflow: hidden;
  background: transparent;
  cursor: pointer;
}

.status-indicator {
  font-size: 0.85rem;
  color: #94a3b8;
  margin-left: auto;
}

.status-indicator[data-status='error'] {
  color: #f87171;
}

.status-indicator[data-status='evaluating'] {
  color: #facc15;
}

.status-indicator[data-status='ready'] {
  color: #34d399;
}

.expression-input {
  width: 100%;
  border-radius: 0.6rem;
  border: 1px solid rgba(148, 163, 184, 0.2);
  padding: 0.75rem;
  background: rgba(15, 23, 42, 0.6);
  color: #e2e8f0;
  font-size: 0.95rem;
  resize: vertical;
  min-height: 3.5rem;
  transition: border 120ms ease, box-shadow 120ms ease;
}

.expression-input:focus {
  outline: none;
  border-color: rgba(56, 189, 248, 0.6);
  box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.25);
}

.expression-footer {
  display: flex;
  justify-content: flex-end;
}

.expression-remove {
  border: none;
  background: rgba(248, 113, 113, 0.15);
  color: #fca5a5;
  border-radius: 0.5rem;
  padding: 0.35rem 0.65rem;
  font-size: 0.85rem;
  cursor: pointer;
  transition: background 120ms ease;
}

.expression-remove:hover {
  background: rgba(248, 113, 113, 0.25);
}

.expression-error {
  margin: 0;
  font-size: 0.8rem;
  color: #f87171;
}

.empty-state {
  padding: 1rem;
  border-radius: 0.75rem;
  border: 1px dashed rgba(148, 163, 184, 0.3);
  text-align: center;
  font-size: 0.9rem;
  color: #94a3b8;
}
</style>
